In [1]:
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.dates as mdates
from astropy.time import Time, TimeDelta
import astropy.constants

c = astropy.constants.c.value

In [2]:
mjd_unixtimestamp_offset = 10587.5
seconds_in_day = 3600 * 24

def mjd2unixtimestamp(m):
    return (m - mjd_unixtimestamp_offset) * seconds_in_day

In [3]:
def load_file(path):
    ncols = 25
    data = np.fromfile(path, sep=' ')
    return data.reshape((data.size // ncols, ncols))

The file below is obtained by running the dslwp_sme.script GMAT script.


In [4]:
def load_data(path, start, end):
    data = load_file(path)
    t = Time(mjd2unixtimestamp(data[:,0]), format='unix')
    sel = (Time(start) <= t) & (t <= Time(end))
    t = t[sel]
    data = data[sel,:]
    return t, data

In [5]:
t, data = load_data('/home/daniel/jupyter_notebooks/dslwp/SME.txt', '2018-10-07T09:50:00', '2018-10-07T12:10:00')
t2, data2 = load_data('/home/daniel/jupyter_notebooks/dslwp/SME2.txt', '2018-10-19T16:50:00', '2018-10-19T18:50:00')

In [6]:
def rangerate(x):
    return np.sum(x[:,:3] * x[:,3:], axis=1) / np.sqrt(np.sum(x[:,:3]**2, axis=1)) * 1e3

def rangerate2doppler(x, freq = 436.4e6):
    return -x * freq / c

In [7]:
direct_doppler = rangerate2doppler(rangerate(data[:,1:7]))
direct_doppler_435 = rangerate2doppler(rangerate(data[:,1:7]), freq = 435.4e6)
direct_t = t

To compute the Moonbounce Doppler, we first compute the length of the reflection path by laying a fine grid of points on the Moon surface and choosing as reflection point the grid point that minimizes the sum of distances between the point and the satellite and Groundstation. The Doppler is computed as the time derivative of the length of the reflection path.


In [8]:
def compute_moonbounce(data):
    long, lat = np.meshgrid(np.linspace(-np.pi, np.pi, 1000), np.linspace(-np.pi/2, np.pi/2, 1000))
    moon_radius = 1737
    moon_x = moon_radius * np.cos(long) * np.cos(lat)
    moon_y = moon_radius * np.sin(long) * np.cos(lat)
    moon_z = moon_radius * np.sin(lat)
    min_dist = np.empty(data.shape[0])
    reflection_point_long = np.empty(data.shape[0])
    reflection_point_lat = np.empty(data.shape[0])
    for j in range(data.shape[0]):
        dist = np.sqrt((data[j,19]-moon_x)**2 + (data[j,20]-moon_y)**2 + (data[j,21]-moon_z)**2) + np.sqrt((data[j,13]-moon_x)**2 + (data[j,14]-moon_y)**2 + (data[j,15]-moon_z)**2) 
        min_dist[j] = np.min(dist)
        minpoint = np.unravel_index(np.argmin(dist), dist.shape)
        reflection_point_long[j] = long[minpoint]
        reflection_point_lat[j] = lat[minpoint]
    return min_dist, reflection_point_long, reflection_point_lat

In [9]:
min_dist, reflection_point_long, reflection_point_lat = compute_moonbounce(data)
min_dist_2, reflection_point_long_2, reflection_point_lat_2 = compute_moonbounce(data2)

In [10]:
plt.figure(figsize = [15,8], facecolor='w')
plt.plot(np.rad2deg(reflection_point_long), np.rad2deg(reflection_point_lat))
plt.plot(np.rad2deg(reflection_point_long_2), np.rad2deg(reflection_point_lat_2))
plt.imshow(mpimg.imread('/home/daniel/GMAT/R2018a/data/graphics/texture/Moon_HermesCelestiaMotherlode.png'), extent=[-180,180,-90,90])
plt.title('Specular reflection point')
plt.xlabel('Longitude (deg)')
plt.ylabel('Latitude (deg)')
plt.legend(['2018-10-07', '2018-10-19']);



In [11]:
moonbounce_rangerate = np.diff(min_dist)/np.diff(mjd2unixtimestamp(data[:,0])) * 1e3
moonbounce_doppler = rangerate2doppler(moonbounce_rangerate)
moonbounce_doppler_435 = rangerate2doppler(moonbounce_rangerate, 435.4e6)
moonbounce_t = Time(mjd2unixtimestamp((data[:-1,0] + data[1:,0])/2), format='unix')

In [12]:
plt.figure(figsize = [15,8], facecolor='w')
plt.plot(direct_t.datetime, direct_doppler)
plt.plot(moonbounce_t.datetime, moonbounce_doppler)
plt.title('DSLWP-B Doppler at 436.4MHz as seen in Dwingeloo')
plt.xlabel('UTC Time')
plt.ylabel('Doppler (Hz)')
plt.legend(['Direct', 'Moonbounce']);



In [13]:
def waterfall_plot(image_path, start_time, timespan, doppler_offset = 750, band435 = False):
    img = mpimg.imread(image_path)
    img_start = Time(start_time)
    img_end = img_start + TimeDelta(timespan, format='sec')
    plt.figure(figsize = [15,8], facecolor='w')
    plt.plot(direct_t.datetime, direct_doppler if not band435 else direct_doppler_435)
    plt.plot(moonbounce_t.datetime, moonbounce_doppler if not band435 else moonbounce_doppler_435)
    if band435:
        for offset in 1000 + 312.5*np.arange(4):
            plt.plot(direct_t.datetime, direct_doppler + offset, color='C0', linewidth=1, linestyle=':')
            plt.plot(moonbounce_t.datetime, moonbounce_doppler + offset, color='C1', linewidth=1, linestyle=':')
    freq = '436.4MHz' if not band435 else '435.4MHz'
    plt.title(f'DSLWP-B Doppler at {freq} and signal recorded in Dwingeloo (offset {-doppler_offset}Hz)')
    plt.xlabel('UTC Time')
    plt.ylabel('Doppler (Hz)')
    plt.legend(['Direct', 'Moonbounce'])

    min_freq = -1000 if not band435 else 900-1800
    max_freq = 1000 if not band435 else 900+1800
    plt.imshow(img, extent = (mdates.date2num(img_start.datetime), mdates.date2num(img_end.datetime), min_freq-doppler_offset, max_freq-doppler_offset),\
               aspect = 'auto');

In [14]:
waterfall_plot('dslwp_sme0.png', '2018-10-07T09:59:13', 2280.8313)



In [15]:
waterfall_plot('dslwp_sme1.png', '2018-10-07T10:37:18', 3203.4544)



In [16]:
waterfall_plot('dslwp_sme2.png', '2018-10-07T11:30:45', 1827.902125)



In [17]:
waterfall_plot('dslwp_sme0_435.png', '2018-10-07T09:59:13', 2280.8313, doppler_offset = 800, band435 = True)



In [18]:
waterfall_plot('dslwp_sme1_435.png', '2018-10-07T10:37:18', 3203.4544, doppler_offset = 800, band435 = True)



In [19]:
waterfall_plot('dslwp_sme2_435.png', '2018-10-07T11:30:45', 1827.902125, doppler_offset = 800, band435 = True)